﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net;
using System.Data.SqlClient;
using System.Threading;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
using System.IO;
using System.Security.Cryptography;

namespace eventModel
{
    class ServerEntry
    {
        // Some internal lists...
        public static List<string> ExecutableExtensions { get; set; }
        public static List<string> HostBindings { get; set; }
        public static bool AllowListen { get; set; }
        private static HttpListener httpLstnrMain { get; set; }

        static void Main(string[] args)
        {
            // Set server settings- should eventually be loaded from a file:
            Pipeline.AllowExecutables = true;
            Pipeline.EnableLogging = true;
            Pipeline.ServerDir = AppDomain.CurrentDomain.BaseDirectory;
            ServerEntry.ExecutableExtensions = new List<string>() { "*.cs" };
            ServerEntry.HostBindings = new List<string>() { "http://*:8080/" };
            Compiler.MaxAllowedCachedCompilations = 50;


            // If we allow executables & have some executable extensions...
            if (Pipeline.AllowExecutables && ExecutableExtensions.Count > 0)
            {
                // Create a new FileSystemWatcher to keep track of file changes:
                FileSystemWatcher watcher = new FileSystemWatcher();
                watcher.Path = Pipeline.ServerDir;
                watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName;

                string fltrs = "";
                ExecutableExtensions.ForEach(ee => fltrs += ee + ",");
                watcher.Filter = fltrs.Substring(0, fltrs.Length - 1);
                watcher.IncludeSubdirectories = true;

                // Add event handlers:
                watcher.Changed += new FileSystemEventHandler(FileSystemChange);
                watcher.Deleted += new FileSystemEventHandler(FileSystemChange);
                watcher.Renamed += new RenamedEventHandler(FileSystemChange);

                // Begin watching:
                watcher.EnableRaisingEvents = true;
            }


            // Start server:
            Console.WriteLine("Starting Server...");
            httpLstnrMain = new HttpListener();
            // Bind to specified port bindings:
            HostBindings.ForEach(b => httpLstnrMain.Prefixes.Add(b));
            httpLstnrMain.Start();

            Console.WriteLine("Server started, now serving.");
            Console.WriteLine("\nAccepted Bindings:");
            httpLstnrMain.Prefixes.ToList().ForEach(p => Console.WriteLine(p));
            Console.WriteLine("\nServing directory: \n" + Pipeline.ServerDir);

            AllowListen = true;
            Thread listenerThread = new Thread(delegate()
                {
                    while (AllowListen)
                    {
                        // Wait till you get a connection:
                        HttpListenerContext cntxt = httpLstnrMain.GetContext();
                        // Pass it on to the thread pool:
                        ThreadPool.QueueUserWorkItem(new WaitCallback(Pipeline.HandleConnection), cntxt);
                    }
                });
            listenerThread.Start();

            //TODO: For easier debugging this opens the compiled test page:
            System.Diagnostics.Process.Start("http://localhost:8080/test.cs");
            
            Console.WriteLine("Server started, now serving. Press any key to quit.");

            ServerInterface.InitializeSystemTrayIcon();

            AllowListen = false;
            listenerThread.Join(1000); // wait 1 second for loop thread to stop.
            //NOTE:  Eventually, we will probably want this to be a configured time interval for each listener, like an allowed 'shutdown' time.
            if (listenerThread.IsAlive) // if listener thread is still running, abort thread PA: Why wouldn't we just always abort the thread without waiting for a "graceful" shutdown?
                listenerThread.Abort();
            httpLstnrMain.Stop();
            httpLstnrMain.Close();
        }

        
        /// <summary>Happens when there is a change in the file system. Update our caches!</summary>
        private static void FileSystemChange(object source, FileSystemEventArgs e)
        { ThreadPool.QueueUserWorkItem(new WaitCallback(CleanUp), e.Name);  }


        /// <summary>Gets ran on a separate thread to do cache cleaning.</summary>
        private static void CleanUp(object filename)
        {
            Compiler.UpdateObjectCache((string)filename);
            Pipeline.CleanBinaryStreamCache((string)filename);
            Pipeline.CleanTextStreamCache((string)filename);
        }
    }
}
